package com.softserveinc.cinema;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import com.softserveinc.R;

public class CinemaSchemaView extends View {

	private static final String TAG = CinemaSchemaView.class.getSimpleName();

	private static final int DEFAULT_ID = 49;

	private static final int DEFAULT_SITS_COUNT = 4;
	private static final int DEFAULT_ROWS_COUNT = 5;

	private static final double DEFAULT_PRICE = 1.;

	private static final int PADDING = 4;

	private final int rows;
	private final int sits;

	private final List<Ticket> tickets = new ArrayList<Ticket>();
	private final List<Drawable> sitsDrawables = new ArrayList<Drawable>();
	private final List<Boolean> booked = new ArrayList<Boolean>();
	private final List<Boolean> checkedList = new ArrayList<Boolean>();

	private int selectedDrawableIndex;

	private boolean canBePressed = true;
	private Drawable selectedDrawable = null;

	private int drawableSize;

	private Drawable marker;

	private double totalPrice = 0.0;

	public CinemaSchemaView(final Context context, final int rows,
			final int sits) {
		super(context);

		this.rows = rows;
		this.sits = sits;

		initView();
	}

	public CinemaSchemaView(final Context context, final AttributeSet attrs) {
		super(context, attrs);

		TypedArray a = context.obtainStyledAttributes(attrs,
				R.styleable.CinemaSchema);

		rows = a.getInt(R.styleable.CinemaSchema_rows, DEFAULT_ROWS_COUNT);
		sits = a.getInt(R.styleable.CinemaSchema_sits, DEFAULT_SITS_COUNT);

		initView();
	}

	private void initView() {

		Log.i(TAG, "init");

		setFocusable(true);
		setClickable(true);
		setSaveEnabled(true);

		if (getId() < 0) {
			setId(DEFAULT_ID);
		}

		for (int i = 0; i < rows; i++) {
			for (int j = 0; j < sits; j++) {
				sitsDrawables.add(getContext().getResources().getDrawable(
						R.drawable.sit_selector));

				checkedList.add(false);
				booked.add(false);

				Ticket ticket = new Ticket();

				ticket.setPrice(DEFAULT_PRICE);
				ticket.setRow(i);
				ticket.setSit(j);

				tickets.add(ticket);
			}
		}
	}

	@Override
	protected void onDraw(final Canvas canvas) {
		super.onDraw(canvas);

		int availWidth = getWidth() - getPaddingLeft() - getPaddingRight();
		int availHeight = getHeight() - getPaddingTop() - getPaddingBottom();

		int drawableWidth = availWidth / sits;
		int drawableHeight = availHeight / rows;

		drawableSize = Math.min(drawableWidth, drawableHeight) - PADDING;

		int totalWidth = (drawableSize + PADDING) * sits - PADDING;
		int offset = (availWidth - totalWidth) / 2;

		Rect rect = new Rect();
		getDrawingRect(rect);

		Paint paint = new Paint();
		paint.setTextAlign(Paint.Align.CENTER);
		paint.setColor(Color.BLACK);

		int index = 0;

		for (int i = 0; i < rows; i++) {
			for (int j = 0; j < sits; j++) {
				Drawable drawable = sitsDrawables.get(index);

				int drawableLeft = rect.left + getPaddingLeft();

				drawableLeft += PADDING * j;
				drawableLeft += drawableSize * j;

				if (offset > 0) {
					drawableLeft += offset;
				}

				int drawableTop = rect.top + getPaddingTop() + PADDING;

				drawableTop += PADDING * i;
				drawableTop += drawableSize * i;

				Rect boundsRect = new Rect(drawableLeft, drawableTop,
						drawableLeft + drawableSize, drawableTop + drawableSize);

				drawable.setBounds(boundsRect);
				drawable.draw(canvas);

				canvas.drawText("" + (j + 1), boundsRect.centerX(),
						boundsRect.centerY(), paint);
				index++;
			}
		}
	}

	@Override
	protected void drawableStateChanged() {
		super.drawableStateChanged();

		if (selectedDrawableIndex > -1) {
			sitsDrawables.get(selectedDrawableIndex).setState(
					getDrawableState());
		}
		invalidate();
	}

	@Override
	protected int[] onCreateDrawableState(final int extraSpace) {
		int[] drawableState = super.onCreateDrawableState(extraSpace + 1);

		if (selectedDrawableIndex > -1) {
			if (booked.get(selectedDrawableIndex)) {
				return new int[] { android.R.attr.state_checked };
			} else {
				if (checkedList.get(selectedDrawableIndex)) {
					drawableState = mergeDrawableStates(drawableState,
							new int[] { android.R.attr.state_checked });
				}
				if (!canBePressed) {
					drawableState = removeDrawableStates(drawableState,
							new int[] { android.R.attr.state_pressed });
				}
			}
		}
		return drawableState;
	}

	@Override
	public boolean onTouchEvent(final MotionEvent event) {

		switch (event.getAction()) {

		case MotionEvent.ACTION_DOWN:
			selectedDrawableIndex = -1;
			for (int i = 0; i < sitsDrawables.size(); i++) {
				if (sitsDrawables.get(i).getBounds()
						.contains((int) event.getX(), (int) event.getY())) {

					selectedDrawableIndex = i;

					selectedDrawable = sitsDrawables.get(i);
					break;
				}
			}
			break;
		case MotionEvent.ACTION_MOVE:

			if (selectedDrawable != null) {
				if (selectedDrawable.getBounds().contains((int) event.getX(),
						(int) event.getY())) {
					canBePressed = true;
					Log.i(TAG, "in");
					refreshDrawableState();
				} else {
					canBePressed = false;
					Log.i(TAG, "out");
					refreshDrawableState();
				}
			}
			break;
		case MotionEvent.ACTION_UP:

			if (selectedDrawable != null) {
				if (selectedDrawable.getBounds().contains((int) event.getX(),
						(int) event.getY())) {

					boolean isChecked = !checkedList.get(selectedDrawableIndex);
					checkedList.set(selectedDrawableIndex, isChecked);

					if (isChecked && !booked.get(selectedDrawableIndex)) {
						totalPrice += tickets.get(selectedDrawableIndex)
								.getPrice();
					} else if (!booked.get(selectedDrawableIndex)) {
						totalPrice -= tickets.get(selectedDrawableIndex)
								.getPrice();
					}
					Log.i(TAG, "total price: " + totalPrice);
				}
			}
			selectedDrawable = null;
			canBePressed = true;
			break;
		}
		return super.onTouchEvent(event);
	}

	private int[] removeDrawableStates(final int[] baseState,
			final int[] stateToRemove) {
		List<Integer> list = new ArrayList<Integer>();

		for (int i = 0; i < baseState.length; i++) {
			int state = baseState[i];
			boolean add = true;
			for (int j = 0; j < stateToRemove.length; j++) {
				if (state == stateToRemove[j]) {
					add = false;
					break;
				}
			}
			if (add) {
				list.add(state);
			}
		}

		int[] result = new int[list.size()];
		for (int i = 0; i < result.length; i++) {
			result[i] = list.get(i);
		}

		return result;
	}

	private void markBookedSits() {
		for (int i = 0; i < booked.size(); i++) {

			if (booked.get(i)) {
				Drawable drawable = sitsDrawables.get(i);

				drawable.setState(new int[] { android.R.attr.state_checked });
			}
		}
	}

	private void markCheckedSits() {
		for (int i = 0; i < checkedList.size(); i++) {

			if (checkedList.get(i)) {
				Drawable drawable = sitsDrawables.get(i);

				drawable.setState(new int[] { android.R.attr.state_checked });
			}
		}
	}

	@Override
	protected Parcelable onSaveInstanceState() {
		Parcelable savedInstanceState = super.onSaveInstanceState();

		SchemaState state = new SchemaState(savedInstanceState);

		state.savedCheckedList.clear();
		state.savedCheckedList.addAll(checkedList);

		state.savedPrice = totalPrice;

		Log.i(TAG, "save state");

		return state;
	}

	@Override
	protected void onRestoreInstanceState(final Parcelable state) {
		SchemaState schemaState = (SchemaState) state;

		super.onRestoreInstanceState(schemaState.getSuperState());

		Log.i(TAG, "restore state");

		checkedList.clear();
		checkedList.addAll(schemaState.savedCheckedList);

		totalPrice = schemaState.savedPrice;

		markCheckedSits();
	}

	static class SchemaState extends BaseSavedState {
		final private List<Boolean> savedCheckedList = new ArrayList<Boolean>();

		private double savedPrice = 0.0;

		SchemaState(final Parcelable parcelable) {
			super(parcelable);
		}

		@SuppressWarnings("unchecked")
		private SchemaState(final Parcel parcel) {
			super(parcel);
			savedCheckedList.clear();
			savedCheckedList.addAll(parcel.readArrayList(null));

			savedPrice = parcel.readDouble();
		}

		@Override
		public void writeToParcel(final Parcel dest, final int flags) {
			super.writeToParcel(dest, flags);
			dest.writeList(savedCheckedList);

			dest.writeDouble(savedPrice);
		}

		public static final Parcelable.Creator<SchemaState> CREATOR = new Parcelable.Creator<SchemaState>() {
			@Override
			public SchemaState createFromParcel(final Parcel in) {
				return new SchemaState(in);
			}

			@Override
			public SchemaState[] newArray(final int size) {
				return new SchemaState[size];
			}
		};
	}

	public void setBookedSits(final List<Boolean> bookedVaues) {
		booked.clear();
		booked.addAll(bookedVaues);

		markBookedSits();
	}

	public List<Boolean> getBookedSits() {
		List<Boolean> result = new ArrayList<Boolean>();

		for (int i = 0; i < booked.size(); i++) {
			result.add(booked.get(i) | checkedList.get(i));
		}

		return result;
	}

	public void setTicketPrice(final double price, final int row, final int sit) {
		if (tickets != null && !tickets.isEmpty()) {
			int sitIndex = sits * (row - 1) + sit - 1;

			Ticket ticket = tickets.get(sitIndex);

			ticket.setPrice(price);
		}
	}

	public Drawable getMarker() {
		return marker;
	}

	public void setMarker(final Drawable marker) {
		this.marker = marker;
	}

	public double getTotalPrice() {
		return totalPrice;
	}

	public void setTotalPrice(final double totalPrice) {
		this.totalPrice = totalPrice;
	}
}
